/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    sdio.c
  * @brief   This file provides code for the configuration
  *          of the SDIO instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "sdio.h"

/* USER CODE BEGIN 0 */
bool My_ReadStatus = false;//读标志，完成true
bool My_WriteStatus = false;//写标志，完成true

#define MY_SD_TIMEOUT 30000
/* USER CODE END 0 */

SD_HandleTypeDef hsd;
DMA_HandleTypeDef hdma_sdio_rx;
DMA_HandleTypeDef hdma_sdio_tx;

/* SDIO init function */

void MX_SDIO_SD_Init(void)
{

  /* USER CODE BEGIN SDIO_Init 0 */

  /* USER CODE END SDIO_Init 0 */

  /* USER CODE BEGIN SDIO_Init 1 */

  /* USER CODE END SDIO_Init 1 */
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.BusWide = SDIO_BUS_WIDE_4B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 4;
  if (HAL_SD_Init(&hsd) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SDIO_Init 2 */

  /* USER CODE END SDIO_Init 2 */

}

void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(sdHandle->Instance==SDIO)
  {
  /* USER CODE BEGIN SDIO_MspInit 0 */

  /* USER CODE END SDIO_MspInit 0 */
    /* SDIO clock enable */
    __HAL_RCC_SDIO_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**SDIO GPIO Configuration
    PC8     ------> SDIO_D0
    PC9     ------> SDIO_D1
    PC10     ------> SDIO_D2
    PC11     ------> SDIO_D3
    PC12     ------> SDIO_CK
    PD2     ------> SDIO_CMD
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* SDIO DMA Init */
    /* SDIO_RX Init */
    hdma_sdio_rx.Instance = DMA2_Stream3;
    hdma_sdio_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_sdio_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_sdio_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sdio_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sdio_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_sdio_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_sdio_rx.Init.Mode = DMA_PFCTRL;
    hdma_sdio_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_sdio_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_sdio_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_sdio_rx.Init.MemBurst = DMA_MBURST_INC4;
    hdma_sdio_rx.Init.PeriphBurst = DMA_PBURST_INC4;
    if (HAL_DMA_Init(&hdma_sdio_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(sdHandle,hdmarx,hdma_sdio_rx);

    /* SDIO_TX Init */
    hdma_sdio_tx.Instance = DMA2_Stream6;
    hdma_sdio_tx.Init.Channel = DMA_CHANNEL_4;
    hdma_sdio_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_sdio_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sdio_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sdio_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_sdio_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_sdio_tx.Init.Mode = DMA_PFCTRL;
    hdma_sdio_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_sdio_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_sdio_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_sdio_tx.Init.MemBurst = DMA_MBURST_INC4;
    hdma_sdio_tx.Init.PeriphBurst = DMA_PBURST_INC4;
    if (HAL_DMA_Init(&hdma_sdio_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(sdHandle,hdmatx,hdma_sdio_tx);

    /* SDIO interrupt Init */
    HAL_NVIC_SetPriority(SDIO_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(SDIO_IRQn);
  /* USER CODE BEGIN SDIO_MspInit 1 */

  /* USER CODE END SDIO_MspInit 1 */
  }
}

void HAL_SD_MspDeInit(SD_HandleTypeDef* sdHandle)
{

  if(sdHandle->Instance==SDIO)
  {
  /* USER CODE BEGIN SDIO_MspDeInit 0 */

  /* USER CODE END SDIO_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SDIO_CLK_DISABLE();

    /**SDIO GPIO Configuration
    PC8     ------> SDIO_D0
    PC9     ------> SDIO_D1
    PC10     ------> SDIO_D2
    PC11     ------> SDIO_D3
    PC12     ------> SDIO_CK
    PD2     ------> SDIO_CMD
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_12);

    HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);

    /* SDIO DMA DeInit */
    HAL_DMA_DeInit(sdHandle->hdmarx);
    HAL_DMA_DeInit(sdHandle->hdmatx);

    /* SDIO interrupt Deinit */
    HAL_NVIC_DisableIRQ(SDIO_IRQn);
  /* USER CODE BEGIN SDIO_MspDeInit 1 */

  /* USER CODE END SDIO_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

HAL_StatusTypeDef My_SDIO_SD_Init_Fixed(void)
{
  HAL_StatusTypeDef result;
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 4;//分频，48/(ClockDiv+2)，硬件设计并不完美，不分频可能有问题

  result = HAL_SD_Init(&hsd);//初始化SD卡
  if ( result != HAL_OK)
  {
    return result;
  }
  result = HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B);//配置总线
  return result;
}

HAL_StatusTypeDef My_SD_ReadBlocks(uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
  // My_ReadStatus = false;//清除标志

  HAL_StatusTypeDef result = HAL_ERROR;
  if(HAL_SD_ReadBlocks(&hsd, pData, BlockAdd, NumberOfBlocks, HAL_MAX_DELAY) == HAL_OK)//注意备注要求使用后检查SD卡状态
  {
    /* wait until the read operation is finished */
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    {}
    result = HAL_OK;
    // My_ReadStatus = true;//设置标志
  }

  return result;
}

HAL_StatusTypeDef My_SD_ReadBlocks_DMA(uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
  HAL_StatusTypeDef result = HAL_ERROR;
  uint32_t timeout;

  /*
  * ensure the SDCard is ready for a new operation
  */
  if(My_SD_CheckStatusWithTimeout(MY_SD_TIMEOUT) != HAL_OK)
    return result;


  if(HAL_SD_ReadBlocks_DMA(&hsd, pData, BlockAdd, NumberOfBlocks) == HAL_OK)
  {
    My_ReadStatus = false;//清除标志位

    /* Wait that the reading process is completed or a timeout occurs */
    timeout = HAL_GetTick();
    while((My_ReadStatus == false) && ((HAL_GetTick() - timeout) < MY_SD_TIMEOUT))
    {
    }
    /* in case of a timeout return error */
    if(My_ReadStatus == false)
    {
      result = HAL_ERROR;
    }
    else
    {
      My_ReadStatus = false;
      timeout = HAL_GetTick();

      while((HAL_GetTick() - timeout) < MY_SD_TIMEOUT)
      {
        if (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
        {
          result = HAL_OK;
          break;
        }
      }
    }
  }

  return result;
}

HAL_StatusTypeDef My_SD_WriteBlocks(uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
  My_WriteStatus = false;//清除标志

  HAL_StatusTypeDef result = HAL_ERROR;

  if(HAL_SD_WriteBlocks(&hsd, pData, BlockAdd, NumberOfBlocks, HAL_MAX_DELAY) == HAL_OK)
  {
    /* wait until the Write operation is finished */
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    {}
    result = HAL_OK;
    My_WriteStatus = true;//设置标志
  }

  return result;
}

HAL_StatusTypeDef My_SD_WriteBlocks_DMA(uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
  HAL_StatusTypeDef result = HAL_ERROR;
  uint32_t timeout;

  /*
  * ensure the SDCard is ready for a new operation
  */
  if(My_SD_CheckStatusWithTimeout(MY_SD_TIMEOUT) != HAL_OK)
    return result;


  if(HAL_SD_WriteBlocks_DMA(&hsd, pData, BlockAdd, NumberOfBlocks) == HAL_OK)
  {
    My_WriteStatus = false;//清除标志位

    /* Wait that writing process is completed or a timeout occurs */
    timeout = HAL_GetTick();
    while((My_WriteStatus == false) && ((HAL_GetTick() - timeout) < MY_SD_TIMEOUT))
    {
    }
    /* in case of a timeout return error */
    if(My_WriteStatus == false)
    {
      result = HAL_ERROR;
    }
    else
    {
      My_WriteStatus = false;
      timeout = HAL_GetTick();

      while((HAL_GetTick() - timeout) < MY_SD_TIMEOUT)
      {
        if (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
        {
          result = HAL_OK;
          break;
        }
      }
    }
  }

  return result;
}

HAL_StatusTypeDef My_SD_CheckStatusWithTimeout(uint32_t timeout)
{
  uint32_t timer = HAL_GetTick();
  /* block until SDIO IP is ready again or a timeout occur */
  while(HAL_GetTick() - timer < timeout)
  {
    if (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
    {
      return HAL_OK;
    }
  }

  return HAL_ERROR;
}

/**
  * @brief 重写SD卡Tx转移已完成的回调回调函数
  * @param hsd: Pointer to SD handle
  * @retval None
  */
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsd);

  My_WriteStatus = true;


  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SD_TxCpltCallback can be implemented in the user file
   */
}

/**
  * @brief 重写SD卡Rx转移已完成的回调回调函数
  * @param hsd: Pointer SD handle
  * @retval None
  */
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsd);

  My_ReadStatus = true;

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SD_RxCpltCallback can be implemented in the user file
   */
}
/* USER CODE END 1 */
